#include <sys/interrupt.h>
#include <sys/global.h>
#include <yonex/types.h>
#include <stddef.h>
#include <sys/printk.h>
#include <assert.h>
#include <yonex/debug.h>
#include <sys/io.h>
#include <stdbool.h>

#define LOGK(fmt, args...)  DEBUGK(fmt, ##args)

#define ENTRY_SZIE  0x30

#define PIC_M_EVEN 0x20 // 主片的偶地址端口
#define PIC_M_OLD  0x21 // 主片的奇地址端口
#define PIC_S_EVEN 0xa0 // 从片的偶地址端口
#define PIC_S_OLD  0xa1 // 从片的奇地址端口
#define PIC_EOI    0x20 // 通知中断控制器中断结束

gate_t idt[IDT_SIZE];
handler_t handler_table[IDT_SIZE];
pointer_t idt_ptr;

extern void schedule();
extern handler_t handler_entry_table[ENTRY_SZIE];

// 异常字符串表
static char *messages[] = {
    "#DE Divide Error",
    "#DB RESERVED",
    "--  NMI Interrupt",
    "#BP Breakpoint",
    "#OF Overflow",
    "#BR BOUND Range Exceeded",
    "#UD Invalid Opcode (Undefined Opcode)",
    "#NM Device Not Available (No Math Coprocessor)",
    "#DF Double Fault",
    "--  Coprocessor Segment Overrun (reserved)",
    "#TS Invalid TSS",
    "#NP Segment Not Present",
    "#SS Stack-Segment Fault",
    "#GP General Protection",
    "#PF Page Fault",
    "--  (Intel reserved. Do not use.)",
    "#MF x87 FPU Floating-Point Error (Math Fault)",
    "#AC Alignment Check",
    "#MC Machine Check",
    "#XF SIMD Floating-Point Exception",
    "#VE Virtualization Exception",
    "#CP Control Protection Exception",
};

// 默认异常处理函数
static void exception_handler(int vector)
{
    char *message = NULL;
    if (vector < 22)
        message = messages[vector];
    else
        message = messages[15]; // intel reserved

    panic("Exception : [%#02x] %s\n", vector, message);
}

// 发送中断处理结束
void send_eoi(int vector)
{
    outb(PIC_M_EVEN, PIC_EOI);
    if (vector >= 0x28)
        outb(PIC_S_EVEN, PIC_EOI);
}

// 默认外中断处理函数
static void interrupt_handler(int vector)
{
    assert(vector >= 0x20 && vector < 0x30);
    send_eoi(vector);

    // schedule();
    LOGK("Interrupt : [%#02x]\n", vector);
}

// 初始化中断控制器
static void pic_init()
{
    LOGK("init pic\n");
    // 初始化主片
    outb(PIC_M_EVEN, 0b00010001);   // ICW1
    outb(PIC_M_OLD,  0x20);         // ICW2
    outb(PIC_M_OLD,  0b00000100);   // ICW3
    outb(PIC_M_OLD,  0b00000001);   // ICW4

    // 初始化从片
    outb(PIC_S_EVEN, 0b00010001);   // ICW1
    outb(PIC_S_OLD,  0x28);         // ICW2
    outb(PIC_S_OLD,  0x02);         // ICW3
    outb(PIC_S_OLD,  0b00000101);   // ICW4

    // 操作命令字
    outb(PIC_M_OLD, 0b11111111);    // OCW1
    outb(PIC_S_OLD, 0b11111111);    // OCW1
}

// 注册异常处理函数
void set_exception_handler(int vector, handler_t handler)
{
    assert(vector >= 0 && vector < 0x16);
    handler_table[vector] = handler;
}

// 注册外中断处理函数
void set_interrupt_handler(int vector, handler_t handler)
{
    assert(vector >= 0x20 && vector < 0x30);
    handler_table[vector] = handler;
}

// 使能/屏蔽外中断
void set_interrupt_mask(int vector, bool enable)
{
    assert(vector >= 0x20 && vector < 0x30);
    u16 port;
    if (vector < 0x28) {
        port = PIC_M_OLD;
        vector -= 0x20;
    }
    else {
        port = PIC_S_OLD;
        vector -= 0x28;
    }
    if (enable)
        outb(port, (inb(port) & ~(1 << vector)));
    else
        outb(port, (inb(port) | (1 << vector)));
}

// 初始化中断向量表
static void idt_init()
{
    LOGK("init idt\n");
    // 初始化中断向量表
    for (size_t i = 0; i < 0x30; i++) {
        gate_t *gate = &idt[i];
        handler_t handler = handler_entry_table[i];
        gate->offset0 = (u64)handler & 0xffff;
        gate->offset1 = ((u64)handler >> 16) & 0xffff;
        gate->offset2 = ((u64)handler >> 32);
        gate->selector.index = 1;
        gate->ist = 0;
        gate->type = 0b1110;
        gate->segment = 0;
        gate->DPL = 0;
        gate->present = 1;
    }
    // 初始化默认异常处理函数 0 ~ 0x1f
    for (size_t i = 0; i < 0x20; i++) {
        handler_table[i] = exception_handler;
    }
    
    // 初始化默认外中断处理函数 0x20 ~ 0x2f
    for (size_t i = 0x20; i < 0x30; i++) {
        handler_table[i] = interrupt_handler;
    }
    
    idt_ptr.base = (u64)(idt);
    idt_ptr.limit = sizeof(idt) - 1;
    asm volatile("lidt idt_ptr\n");
}

void interrupt_init()
{
    idt_init();
    pic_init();
}
